package event

import (
	"context"
	"sync"
	"sync/atomic"
)

const (
	defaultCmdQueueSize = 1024
)

var evNum = int32(0)

type IEventReactor interface {
	OnBeforeHandle(id int32, evCmd int64, extras []interface{})
	OnCmdHandle(id int32, evCmd int64, extras []interface{})
	OnAfterHandle(id int32, evCmd int64, extras []interface{})
	OnFinishEventLoop(id int32)
	OnBeginEventLoop(id int32)
}

type DefaultEventReactor struct {
}

func (d DefaultEventReactor) OnBeforeHandle(id int32, evCmd int64, extras []interface{}) {
}

func (d DefaultEventReactor) OnCmdHandle(id int32, evCmd int64, extras []interface{}) {
}

func (d DefaultEventReactor) OnAfterHandle(id int32, evCmd int64, extras []interface{}) {
}

func (d DefaultEventReactor) OnFinishEventLoop(id int32) {
}

func (d DefaultEventReactor) OnBeginEventLoop(id int32) {
}

//EventCommand 事件指令载体
type EventCommand struct {
	EvCmd  int64
	Extras []interface{}
}

type EventLoop struct {
	cxt    context.Context
	cancel context.CancelFunc
	wg     sync.WaitGroup

	evCommand chan *EventCommand
	reactor   IEventReactor
}

func NewEventLoop(cxt context.Context, reactor IEventReactor) *EventLoop {
	cancel, cFunc := context.WithCancel(cxt)

	return &EventLoop{
		cxt:     cancel,
		cancel:  cFunc,
		wg:      sync.WaitGroup{},
		reactor: reactor,
	}
}

func (self *EventLoop) verify() {
	if self.evCommand == nil {
		self.evCommand = make(chan *EventCommand, defaultCmdQueueSize)
	}
}

func (self *EventLoop) InitCmdQueueSize(queSize uint32) {
	if self == nil {
		return
	}

	if self.evCommand == nil {
		self.evCommand = make(chan *EventCommand, queSize)
	}
}

func (self *EventLoop) PostMsg(evCmd int64, extras ...interface{}) {
	if self == nil {
		return
	}

	evCommand := &EventCommand{
		EvCmd:  evCmd,
		Extras: extras,
	}
	self.PostCmd(evCommand)
}

func (self *EventLoop) PostCmd(cmd *EventCommand) {
	if self == nil {
		return
	}

	self.verify()

	self.evCommand <- cmd
}

func (self *EventLoop) running(id int32) {
	if self == nil {
		return
	}
	self.verify()
	self.wg.Add(1)

	self.reactor.OnBeginEventLoop(id)

exit:
	for {
		//select 是具有顺序的
		select {
		case c := <-self.evCommand:
			self.reactor.OnBeforeHandle(id, c.EvCmd, c.Extras)
			self.reactor.OnCmdHandle(id, c.EvCmd, c.Extras)
			self.reactor.OnAfterHandle(id, c.EvCmd, c.Extras)
			break

		case <-self.cxt.Done():
			break exit
		}
	}

	self.reactor.OnFinishEventLoop(id)
	self.wg.Done()
}

func (self *EventLoop) Launch() int32 {
	if self == nil {
		return -1
	}

	id := atomic.AddInt32(&evNum, 1)
	go self.running(id)
	return id
}

func (self *EventLoop) Wait() {
	self.wg.Wait()
}

func (self *EventLoop) Stop() {
	self.cancel()
}

func (self *EventLoop) StopAndWait() {
	self.cancel()
	self.wg.Wait()
}
